<?php
//define token
require_once 'config.php';

$wechatObj = new WeixinHandler();
// $wechatObj->valid();
$wechatObj->responseMsg();

class WeixinHandler
{
	// 微信服务器外部服务器首次通信验证，已通过测试
    public function valid()
    {
        $echoStr = $_GET["echostr"];

        //valid signature , option
        if($this->checkSignature()){
        	echo $echoStr;
        	exit;
        }
    }

    /** 
      * 发送响应消息
      * 对微信服务器推送给外部服务器的消息进行响应的唯一公共接口；
      * 目前该接口只响应文本事件和自定义菜单点击事件；
      **/
    public function responseMsg()
    {
		//get post data, May be due to the different environments
        // $GLOBALS["HTTP_RAW_POST_DATA"]已在php7中被废弃
		$postStr = file_get_contents("php://input");
      	//extract post data
		if (!empty($postStr))
        {
            /* libxml_disable_entity_loader is to prevent XML eXternal Entity Injection,
               the best way is to check the validity of xml by yourself */
            libxml_disable_entity_loader(true);
          	$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            // file_put_contents("msg.txt", $postObj->MsgType);
            switch ($postObj->MsgType) {
                case 'text':
                    $this->respondToText($postObj);
                    break;
                case 'image':
                    $this->respondToImage($postObj);
                    break;
                case 'voice':
                    $this->respondToVoice($postObj);
                    break;
                case 'video':
                    $this->respondToVideo($postObj);
                    break;
                case 'shortvideo':
                    $this->respondToShortvideo($postObj);
                    break;
                case 'location':
                    $this->respondToLocation($postObj);
                    break;
                case 'link':
                    $this->respondToLink($postObj);
                    break;
                case 'event':
                    $this->respondToEvent($postObj);
                    break;                
                default:
                    echo "";
                    exit;
            }
        }
        else 
        {
        	echo "";
        	exit;
        }
    }
	
    // 检查签名，仅用于初次验证，已通过测试	
	private function checkSignature()
	{
        // you must define TOKEN by yourself
        if (!defined("TOKEN")) {
            throw new Exception('TOKEN is not defined!');
        }
        
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];
        		
		$token = TOKEN;
		$tmpArr = array($token, $timestamp, $nonce);
        // use SORT_STRING rule
		sort($tmpArr, SORT_STRING);
		$tmpStr = implode( $tmpArr );
		$tmpStr = sha1( $tmpStr );
		
		if( $tmpStr == $signature ){
			return true;
		}else{
			return false;
		}
	}

    /** 
      * 发送文本消息，已通过测试
      * @param $obj：   微信给服务器发送的消息对象,通过simplexml_load_string()函数获取
      * @param $text：  待发送的文本字符串
      **/
    private function sendText($obj, $text)
    {
        $textTpl = "<xml>
                    <ToUserName><![CDATA[%s]]></ToUserName>
                    <FromUserName><![CDATA[%s]]></FromUserName>
                    <CreateTime>%s</CreateTime>
                    <MsgType><![CDATA[text]]></MsgType>
                    <Content><![CDATA[%s]]></Content>
                    </xml>";
        $wx_msg = sprintf($textTpl, $obj->FromUserName, $obj->ToUserName, time(), $text);
        echo $wx_msg;
    }

    /** 
      * 发送图文消息，已通过测试
      * @param $obj：   微信给服务器发送的消息对象,通过simplexml_load_string()函数获取
      * @param $news：  待发送的图文消息
      * 数据格式说明：
      * $news = [$item_1,$item_2,...,$item_n];
      * 一个$news中最多可放8-10个消息；
      * 数据示例：
      * $item1 = ['Title'=>"标题1",'Description'=>'消息描述1', "PicUrl"=>"图片链接1", "Url" =>"跳转地址1"];
      * $item2 = ['Title'=>"标题2",'Description'=>'消息描述2', "PicUrl"=>"图片链接2", "Url" =>"跳转地址2"];
      * $news = [$item1, $item2];
      **/
    private function sendNews($obj, $news)
    {
        $news_header = "<xml>
                        <ToUserName><![CDATA[%s]]></ToUserName>
                        <FromUserName><![CDATA[%s]]></FromUserName>
                        <CreateTime>%s</CreateTime>
                        <MsgType><![CDATA[news]]></MsgType>
                        <ArticleCount>%s</ArticleCount>
                        <Articles>";

        $news_item =   "<item>
                        <Title><![CDATA[%s]]></Title>
                        <Description><![CDATA[%s]]></Description>
                        <PicUrl><![CDATA[%s]]></PicUrl>
                        <Url><![CDATA[%s]]></Url>
                        </item>";
        $len = count($news);
        echo sprintf($news_header,$obj->FromUserName,$obj->ToUserName,time(),$len);
        for ($i=0; $i < $len; $i++) 
        { 
            echo "<item>";
            if(isset($news[$i]['Title']))
            {
                $title = $news[$i]['Title'];
                echo "<Title><![CDATA[$title]]></Title> ";
            }
            if(isset($news[$i]['Description']))
            {
                $des = $news[$i]['Description'];
                echo "<Description><![CDATA[$des]]></Description> ";
            }
            if(isset($news[$i]['PicUrl']))
            {
                $picurl = $news[$i]['PicUrl'];
                echo "<PicUrl><![CDATA[$picurl]]></PicUrl> ";
            }
            if(isset($news[$i]['Url']))
            {
                $url = $news[$i]['Url'];
                echo "<Url><![CDATA[$url]]></Url> ";
            }
            echo "</item>";
        }
        echo "</Articles></xml>"; 
    }

    // 发送音乐，接口待测试

    // private sendMusic($obj,$music)
    // {
    //     $musicTpl = '<xml>
    //                    <ToUserName><![CDATA[%s]]></ToUserName>
    //                    <FromUserName><![CDATA[%s]]></FromUserName>
    //                    <CreateTime>%s</CreateTime>
    //                    <MsgType><![CDATA[music]]></MsgType>
    //                    <Music>
    //                    <Title><![CDATA[%s]]></Title>
    //                    <Description><![CDATA[%s]]></Description>
    //                    <MusicUrl><![CDATA[%s]]></MusicUrl>
    //                    <HQMusicUrl><![CDATA[%s]]></HQMusicUrl>
    //                    <ThumbMediaId><![CDATA[%s]]></ThumbMediaId>
    //                    </Music>
    //                    </xml>';
    //     $wx_msg = sprintf($musicTpl, $obj->FromUserName, $obj->ToUserName, time(), $text);
    // }


    // 在此自定义对文本消息的响应，接口已测试
    private function respondToText($obj)
    {
        $keyword = $obj->Content;
        $rply = $this->getReplyFormTuring($keyword, $obj->FromUserName);
        $this->turingMsgHandler($obj,$rply);
        
    }

    // 在此自定义对图片消息的响应，接口已测试
    private function respondToImage($obj)
    {
        //todo
        $this->sendText($obj,"暂时无法处理图片~");
    }

    // 在此自定义对文语音消息的响应，接口已测试
    private function respondToVoice($obj)
    {
        $keyword = $obj->Recognition;
        $rply = $this->getReplyFormTuring($keyword, $obj->FromUserName);
        $this->turingMsgHandler($obj,$rply);
        
    }

    // 在此自定义对视频消息的响应，接口已测试
    private function respondToVideo($obj)
    {
        //todo
        $this->sendText($obj,"暂时无法处理视频~");
    }

    // 在此自定义对小视频消息的响应，接口已测试
    private function respondToShortvideo($obj)
    {
        //todo
        $this->sendText($obj,"暂时无法处理小视频~");
    }

    // 在此自定义对视地理位置消息的响应，接口已测试
    private function respondToLocation($obj)
    {
        //todo
        $this->sendText($obj,"暂时无法处理地理位置~");
    }

    // 在此自定义对视地理位置消息的响应，接口已测试
    private function respondToLink($obj)
    {
        //todo
        $this->sendText($obj,"暂时无法处理链接~");
    }

    // 事件响应接口，目前无需更改，接口已测试
    private function respondToEvent($obj)
    {
        switch ($obj->Event) {
            // 请勿在此直接更改，将自定义事件写在 respondToSubscribeEvent($obj) 函数中
            case 'subscribe':
                $this->respondToSubscribeEvent($obj);
                break;
            case 'unsubscribe':
                # do something here;
                break;
            case 'CLICK':
            // 请勿在此直接更改，将自定义事件写在 respondToClickEvent($obj) 函数中
                $this->respondToClickEvent($obj); 
                break;
            case 'VIEW':
            // 请勿在此直接更改，将自定义事件写在 respondToViewEvent($obj) 函数中
                $this->respondToViewEvent($obj);
                break;
            default:
                break;
        }
    }

    // 在此自定义用户关注公众号响应事件，接口已测试
    private function respondToSubscribeEvent($obj)
    {
        $msg = "感谢关注mathmatrix，公众号的作者是个懒人，偶尔会有推送。不过公众号已经接入了图灵机器人小Charlie，所以如果你愿意，可以调戏机器人小charlie。";
        $this->sendText($obj, $msg);
    }

    // 在此自定义CLICK点击响应事件，接口已测试
    private function respondToClickEvent($obj)
    {
        switch ($obj->EventKey) {
            // 在此编写自定义view菜单响应事件
            case 'CLICKKEY':
                # do something here；
                break;
            // default 中的内容仅做测试用，编写自定义事件后修改
            default:
                $this->sendText($obj, "你刚刚点击了一个点击按钮，将触发一个管理员设定的事件。");
                break;
        }
    }

    // 在此自定义VIEW点击响应事件
    private function respondToViewEvent($obj)
    {
        switch ($obj->EventKey) {
            // 在此编写自定义view菜单响应事件
            case 'VIEWKEY':
                # do something here
                break;
            // default 中的内容仅做测试用，编写自定义事件后修改
            default:
                $this->sendText($obj, "你刚刚点击了一个跳转按钮，将触发一个管理员设定的事件。");
                break;
        }
    }

    private function getReplyFormTuring($keyword,$userid=null)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, TURING_URL."?key=".TURING_KEY."&info=$keyword&userid=$userid");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        $output = curl_exec($ch);
        curl_close($ch);
        return json_decode($output,true);
    }

    private function turingMsgHandler($obj,$rply)
    {
        switch ($rply['code']) {
            case 100000:
                $this->sendText($obj, $rply['text']);
                break;
            case 200000:
                $msg = $rply['text']."\n".$rply['url'];
                $this->sendText($obj, $msg);
                break;
            case 302000:
                $news = array();
                foreach ($rply['list'] as $value) {
                    $item = ['Title'=>$value['article'],
                             'Description'=>"来源：".$value['source'],
                             'PicUrl'=>$value['icon'],
                             'Url'=>$value['detailurl']];
                    array_push($news, $item);
                }
                $this->sendNews($obj, $news);
                break;
            case 308000:
                $news = array();
                foreach ($rply['list'] as $value) {
                    $item = ['Title'=>$value['name'],
                             'Description'=>$value['info'],
                             'PicUrl'=>$value['icon'],
                             'Url'=>$value['detailurl']];
                    array_push($news, $item);
                }
                $this->sendNews($obj, $news);
                break;
            case 40004:
                $this->sendText($obj, "机器人已经休息了，明天再来可好？");
                break;
            default:
                $this->sendText($obj, "我不知你在说什么->_<-");
                break;
        }
    }
}

?>